<?php
use Workerman\Worker;
use Workerman\Lib\Timer;

require_once './Workerman/MySQL/Connection.php';
require_once './Workerman/Autoloader.php';

/*数据结构定义部分-begin*/
//定义工作进程个数
define('WORKER_COUNT',4);
//定义消息长度的最大值
define('MAX_LENGTH_OF_MSG',250);
//定义连接队列处理器的周期
define('CONN_HANDLER_CIRCLE',10);  //单位:ms
//定义消息队列处理器的周期
define('MSG_HANDLER_CIRCLE',10); //单位:ms
//定义心跳存活的最大时长
define('HEART_LIVE_TIME',60000); //单位:ms
//连接等待队列
define('CON_WAITTING_QUEUE','con_waitting_queue');
define('CON_WAITTING_EXPIRES',3);  //单位:ms
define('MAX_LENGTH_OF_CON',500);
//定义定时发送客户列表给客服的时间
define('CUSTOMER_INFO_SEND_CIRCLE',4);  //单位秒
/**
 *@param con_waitting_queue:array(
 *    [0]=>array(
 *        'conn'=>(connection_obj)'',
 *        'time'=>(Time)'',
 *    );
 *    ...
 *);
 */
//消息等待队列
define('MESSAGE_WAITTING_QUEUE','message_waitting_queue');
define('MSG_WAITTING_EXPIRES',60000);//单位:ms
/**
 *@param message_waitting_queue:array(
 *   [0]=>(message)$message,
 *   ...
 *);
 */
//客服、客户匹配队列
define('C_S_MATCH_QUEUE','c_s_match');
/**
 *@param c_s_match:array(
 *  [0]=>array(
 *      'c_ip'=>'',
 *      's_no'=>'',
 *  ),
 *  ...
 *);
 */
//状态列表
define('STATUS_TYPES','status');
/**
 *@param status:array(
 *    'ACTIVE'=>1,
 *    'REMOVED'=>-1,
 *    'BUSY'=>2,
 *    'SPARE'=>3,
 *    'WAITING'=>4,
 *);
 */
define('ACTIVE',1);
define('REMOVED',-1);
define('BUSY',2);
define('SPARE',3);
define('WAITTING',4);
//消息状态列表
define('MESSAGE_STATUS_TYPE','message_state_list');
define('READED',3);
define('SENDED',1);
define('RECIVED',2);
define('SEND_FALI',-1);
define('RECIVE_FAIL',-2);
define('PING_FAIL',-3);
/**
 *@param stuff:array(
 *    ['stuff_no']=>array(
 *        'no'=>stuff_no,
 *        'con'=>con_obj,
 *        'matcher'=>array(
 *             'customer_ip'=>array(
 *                  customer_ip,
 *                 'con'=>con_obj,
 *                 'status'=>STATUS_TYPES[n],
 *                 'time'=>Time
 *              ),
 *             ...
 *         ),
 *         'chat_record'=>array(
 *             'customer_ip'=>message,
 *              ...
 *          ),
 *         'status'=>STATUS_TYPES[n],
 *         'time'=>Time,
 *    ),
 *    ...
 *);
 *
 */
//消息类型
define('MESSAGE_TYPE_LIST','message_type');
define('BT','心跳检测消息');
/**
 *@param bt_message:array(
 *    'type'=>'bt',
 *    'user_type'=>CUSTOMER || STUFF,
 *    'id'=>$customer_ip || $stuff_no
 *);
 */
define('MS','信息消息');
/**
 *@param ms_message:array(
 *    'type'=>'ms',
 *    'sender_type'=>CUSTOMER || STUFF,
 *    'receiver_type'=>CUSTOMER || STUFF,
 *    'sender_id'=>$customer_ip || $stuff_no,
 *    'receiver_id'=>$customer_ip || $stuff_no,
 *    'content'=>'',
 *    'send_time'=>Time,
 *    'received_time'=>Time,
 *    'status'=>MESSAGE_STATUS_TYPE
 *);
 */
define('LOGOUT','退出消息');
/**
 *@param logout:array(
 *    'type'=>'logout',
 *    'user_type'=>CUSTOMER || STUFF,
 *    'id'=> $customer_ip || $stuff_no
 *);
 **/
define('LOGIN','登录');
/**
 *@param login:array(
 *   'type'=>'login',
 *   'user_type'=>CUSTOMER || STUFF,
 *   'id'=>$customer_ip || $stuff_no,
 *);
 */
define('PING','侦测对方的活跃状态');
/**
 *@param ping:array(
 *    'type'=>'ping',
 *    'countpart_type'=>CUSTOMER || STUFF,
 *    'countpart_id'=>$customer_ip || $stuff_no,
 *);
 */
define('TRANS','客服转接另一个客服');
/**
 * @param  trans:array(
 *    'type'=>'trans',
 *    'cur_stuff_no'=>'',
 *    'trans_stuff_no'=>'',
 *    'trans_customer_ip'=>''
 * );
 */
//消息主体类型
define('CUSTOMER',1);
define('STUFF',2);
define('UNKOWN',-1);
/*定义数据结构-end*/


/*主逻辑部分-begin*/
$sc_server=new Worker("websocket://0.0.0.0:8910");
$sc_server->count=WORKER_COUNT;
//$sc_server->transport='udp';
$sc_server->reusePort=true;

$sc_server->onMessage=function($connection,$data){
    global $sc_server;
    global $con_waitting_queue;
    global $message_waitting_queue;
    global $c_s_match;
    global $stuff;
    global $redis;
    global $db;

    //print(11111111111111);
    $data=(array)json_decode($data);

    if(!isset($data['type'])){
        $connection->send('数据请求出错');
        return;
    }
    $user_type=checkUserType($data);
    if($user_type==UNKOWN){
        $connection->send("未知用户类型");
        return;
    }
    $allow_type=array('ms','ping','bt');
    if($data['type']!='login' && !isLogin($user_type,$data) && !in_array($data['type'],$allow_type)){
        $connection->send('请先登录，亲~');
        return;
    }
    if($data['type']=='login'){
        $user_type=checkUserType($data);
        switch($user_type){
            case UNKOWN:
              $connection->send("未知用户类型");
              return;
              break;
            case CUSTOMER:
              $u=existCustomer($data['customer_ip']);
              if($u !== false){
                  //更新新链接，防止刷新接收不到数据
                  $stuff[$c_s_match[$u]['stuff_no']]['matcher'][$data['customer_ip']]['con']=$connection;
                  $connection->send("你已经登录过啦，不要重复登录啦");
                  return;
              }
              $assign_stuff_no=assignStuff();
              if(!$assign_stuff_no){
                  if(!isFullOfCon()){
                      array_push($con_waitting_queue,array('connection'=>$connection,'time'=>time(),'data'=>$data));
                      $connection->send('请稍后亲，客服都处于占线中，正在为你连线中');
                      return;
                  }else{
                      $connection->send('亲，请过几分钟后重新连接，正处于咨询高峰期');
                      return;
                  }
              }else{
                if(!isset($data['customer_ip'])){
                    $connection->send('无法识别用户');
                    return;
                }
                $u=existCustomer($data['customer_ip']);
                if($u !== false){
                    //更新新链接，防止刷新接收不到数据
                    $stuff[$c_s_match[$u]['stuff_no']]['matcher'][$data['customer_ip']]['con']=$connection;
                    $connection->send("你已经登录过啦，不要重复登录啦");
                    return;
                }else{

                    if(notifyStuff($data['customer_ip'],$assign_stuff_no,"客户:".$data['customer_ip']."正在和你通话")){

                        $stuff[$assign_stuff_no]['matcher'][$data['customer_ip']]=createCustomer($data['customer_ip'],$connection);
                        array_push($c_s_match,array('customer_ip'=>$data['customer_ip'],'stuff_no'=>$assign_stuff_no));
                        $stuff[$assign_stuff_no]['status']=BUSY;
                        $stuff[$assign_stuff_no]['matcher'][$data['customer_ip']]['status']=BUSY;

                        $connection->send("登录成功,你可以开始咨询啦,为你安排编号为".$assign_stuff_no."的客服");
                    }else{
                        $connection->send("分配客服失败，请稍后重新连接");
                    }
                    //调用客户处理（装饰者模式）
                    //$stuff[$assign_stuff_no]['con']->send("客户:".$data['customer_ip']."正在和你通话");
                }
              }
              break;
           case STUFF:
              if(!isset($data['stuff_no'])){
                  $connection->send('未知客服');
                  return;
              }
              if(existStuff($data['stuff_no']) !== false){
                  //更新新链接，防止刷新页面断链
                  $stuff[$data['stuff_no']]['con']=$connection;
                  $connection->send('你已经登录过啦');
                  return;
              }else{
                  $stuff[$data['stuff_no']]=createStuff($data['stuff_no'],$connection);
                  $connection->send('登录成功');
              }
              break;
        }

    }else if($data['type']=='bt'){
        //更新时间
        $u_t=null;
        if($user_type==CUSTOMER){
           $u_t=updateCustomerExpires($customer_ip);
        }else if($user_type==STUFF){
           $u_t=updateStuffExpires($stuff_no);
        }else{
          $connection->send("检测不到你的心跳");
        }

    }else if($data['type']=='ms'){
        if(!checkRawMsgData($data)){
            $connection->send('消息格式不对');
        }
        $result=sendMessage($data['sender_type'],$data['receiver_type'],$data['sender_id'],$data['receiver_id'],$data['content']);
        if(!$result){
            //放入消息队列中
            $data['status']=SEND_FALI;

            array_push($message_waitting_queue,array('data'=>$data,'time'=>time()));
        }
    }else if($data['type']=='ping'){
        if(!isset($data['countpart_id']) || !isset($data['countpart_type'])){
            $connection->send(PING_FAIL);
        }
        pong(getMe(),$data['countpart_type'],$data['countpart_id']);
    }else if($data['type']=='logout'){
        logout(getMe());
    }else if($data['type']=='trans'){
        if(!checkTransData($data)){
            $connection->send("转换出错，请检查转换参数");
        }
        if(!existStuff($data['cur_stuff_no'])){
            $connection->send("请你登录");
        }
        if(!checkMatches(STUFF,CUSTOMER,$data['cur_stuff_no'],$data['trans_customer_ip'])){
            $connection->send("被转接的客户目前与你没有关系");
        }
        if(!existStuff($data['trans_stuff_no'])){
            $connection->send("你要转接的客服没上线或者不存在");
        }
        if(transStuff($cur_stuff_no,$trans_stuff_no,$trans_customer_ip)){
            $connection->send("你的客户转接到客服:".$trans_stuff_no."了");
            $stuff[$trans_stuff_no]['con']->send("你接到由客服:".$cur_stuff_no."转接过来的客户".$trans_customer_ip);
            $stuff[$cur_stuff_no]['matcher'][$trans_customer_ip]['con']->send("客服".$cur_stuff_no."将你转接到客服".$trans_stuff_no."上");
        }else{
            $connection->send("转接失败");
        }
        return;
    }else{
        $connection->send('数据请求出错');
        return;
    }

    print(json_encode($message_waitting_queue));

};

$sc_server->onWorkerStart=function($worker){
    global $sc_server;
    global $con_waitting_queue;
    global $message_waitting_queue;
    global $c_s_match;
    global $stuff;
    global $redis;
    global $db;

    if(!extension_loaded('Redis')){
        return;
    }
    $sc_server=array();
    $con_waitting_queue=array();
    $message_waitting_queue=array();
    $c_s_match=array();
    $stuff=array();
    $redis=new Redis();
    $db=new \Workerman\MySQL\Connection('127.0.0.1',3306,'root','root123','shop');
    $redis->connect('127.0.0.1',6379);
    //消息队列处理器的加载
    if($worker->id === 0){
      Timer::add(MSG_HANDLER_CIRCLE,'msgQueueHandler',$worker);
    }
    //连接队列处理器
    if($worker->id === 0){
      Timer::add(CONN_HANDLER_CIRCLE,'conQueueHandler',$worker);
    }
    //用户心跳存活处理器
    if($worker->id === 1){
      Timer::add(10,'heartDetectHandler',$worker);
    }
    //redis会话更新处理器
    if($worker->id === 2){
      Timer::add(1800,'redisUpdateCommHandler',$worker);
    }
    //redis加载刷新全部客服列表处理器
    if($worker->id === 2){
      Timer::add(1200,'redisUpdateStuffHandler',$worker);
    }
    //向在线客服定时推送其对应的客户信息
    if($worker->id === 3){
      Timer::add(4,'customerSendToStuffHandler',$worker);
    }
};

Worker::runAll();
/*主逻辑部分-end*/



/*处理器函数部分-begin*/

//连接队列处理器 分配到worker1中
function conQueueHandler($worker){
    global $con_waitting_queue;
    global $stuff;
    global $c_s_match;

    //已过期的连接  //等待被处理的连接   //正在处理的连接
      for($i=0;$i<count($con_waitting_queue);$i++){
          $ts=time();
          if($con_waitting_queue[$i]['time']+CON_WAITTING_EXPIRES < $ts){
              $con_waitting_queue[$i]['connection']->send('请重新登录');
              array_splice($con_waitting_queue,$i,1);
          }else{
              $o=existCustomer($con_waitting_queue[$i]['data']['customer_ip']);
              if($o !== false){
                  $con_waitting_queue[$i]['connection']->send("你正在和客服:".$c_s_match[$o]['stuff_no']."沟通");
                  continue;
              }
              $assign_stuff_no=assignStuff();
              if(!$assign_stuff_no){
                  if(!isFullOfCon()){
                      //array_push($con_waitting_queue,array('connection'=>$connection,'time'=>time(),'data'=>$data));
                      $con_waitting_queue[$i]['connection']->send('请稍后亲，客服都处于占线中，正在为你连线中');
                      return;
                  }else{
                      $con_waitting_queue[$i]['connection']->send('亲，请过几分钟后重新连接，正处于咨询高峰期');
                      return;
                  }
              }else{
                  $data=$con_waitting_queue[$i]['data'];
                  if(!isset($data['customer_ip'])){
                      $con_waitting_queue[$i]['connection']->send('无法识别用户');
                      return;
                  }
                  $u=existCustomer($data['customer_ip']);
                  if($u !== false){
                      //$stuff[$c_s_match[$u]['stuff_no']]['matcher'][$data['customer_ip']]['con']=$connection;
                      $con_waitting_queue[$i]['connection']->send("你已经登录过啦，不要重复登录啦");
                      array_splice($con_waitting_queue,$i,1);
                      unset($data);
                      return;
                  }else{

                      if(notifyStuff($data['customer_ip'],$assign_stuff_no,"客户:".$data['customer_ip']."正在和你通话")){

                          $stuff[$assign_stuff_no]['matcher'][$data['customer_ip']]=createCustomer($data['customer_ip'],$con_waitting_queue[$i]['connection']);
                          array_push($c_s_match,array('customer_ip'=>$data['customer_ip'],'stuff_no'=>$assign_stuff_no));
                          $stuff[$assign_stuff_no]['status']=BUSY;
                          $stuff[$assign_stuff_no]['matcher'][$data['customer_ip']]['status']=BUSY;

                          $con_waitting_queue[$i]['connection']->send("登录成功,你可以开始咨询啦,为你安排编号为".$assign_stuff_no."的客服");
                          array_splice($con_waitting_queue,$i,1);
                      }else{
                          $con_waitting_queue[$i]['connection']->send("分配客服失败，请稍后重新连接");
                      }
                      //调用客户处理（装饰者模式）
                      //$stuff[$assign_stuff_no]['con']->send("客户:".$data['customer_ip']."正在和你通话");
                  }
                  unset($data);
            }
          }
      }
}

//消息队列处理器，分配到worker1中
function msgQueueHandler($worker){
    global $message_waitting_queue;
    global $c_s_match;
    global $stuff;
    if(count($message_waitting_queue)==0){
        return;
    }
    for($i=0;$i<count($message_waitting_queue);$i++){
        $ts=time();
        if($message_waitting_queue[$i]['time']+MSG_WAITTING_EXPIRES < $ts){
            array_splice($message_waitting_queue,$i,1);
        }else{
          if(!checkRawMsgData($message_waitting_queue[$i]['data'])){
              array_splice($message_waitting_queue,$i,1);
          }
          $data=$message_waitting_queue[$i]['data'];
          $result=sendMessage($data['sender_type'],$data['receiver_type'],$data['sender_id'],$data['receiver_id'],$data['content']);
          unset($data);
          if($result){
              array_splice($message_waitting_queue,$i,1);
          }
        }
    }
}
//用户心跳存活处理器 分配到worker2中
function heartDetectHandler($worker){
  global $stuff;

    $cur_pos=0;
    foreach($stuff as $s_k=>$s_v){
        if($s_v['time']+HEART_LIVE_TIME > time()){
            array_splice($stuff,$cur_pos,1);
        }else{
            $n_pos=0;
            foreach($s_v['matcher'] as $c_k=>$c_v){
                if($c_v['time']+HEART_LIVE_TIME > time()){
                    //unset($stuff[$s_k]['matcher'][$c_k]);
                    array_splice($stuff[$s_k]['matcher'],$n_pos,1);
                }
                $n_pos++;
            }
        }
        $cur_pos++;
    }
    unset($n_pos);
    unset($cur_pos);
}
//用户状态检测处理器 分配到worker2中
function userStatusDetectHandler($worker){

}
//redis会话更新处理器 分配到worker3中
function redisUpdateCommHandler($worker){
    global $redis;
    global $stuff;
    //采用集合Set
    foreach($stuff as $s_k=>$s_v){
        $c_pos=0;
        for($i=0;$i<count($s_v['chat_record']);$i++){
            if($s_v['chat_record'][$i]['status']==READED){
                $redis->sadd($s_k,json_encode($chat));
                 array_splice($stuff,$c_pos,1);
            }
            $c_pos++;
        }
    }
    unset($c_pos);
}
//redis加载刷新全部客服列表处理器 分配到worker3中
function redisUpdateStuffHandler($worker){
    global $redis;
    global $db;

    $stuffs=$db->select('stuff_no')->from(STUFF)->query();

    for($i=0;$i<count($stuffs);$i++){
        $redis->sadd('stuffs',$stuffs[$i]);
    }
}
//mysql数据库会话更新处理器 分配到worker4中
function mysqlUpdateCommHandler($worker){

}
//mysql数据加载会话数据处理器 分配到worker4中
function mysqlDealCommHandler($worker){

}
//定时将客户在线数据传输给客服
function customerSendToStuffHandler($worker){
    global $stuff;
    global $c_s_match;
    //s_info标识
    foreach($stuff as $k=>$v){
        $stuff[$k]['con']->send(json_encode(array('s_info'=>$v['matcher'])));
    }
}
/*处理器函数部分-end*/



/*辅助函数部分-begin*/

//存在并发问题
//判断连接队列是否已满
/**
 *@param void
 *@return boolean
 **/
function isFullOfCon(){
  global $con_waitting_queue;
  if(count($con_waitting_queue) < MAX_LENGTH_OF_CON){
      return false;
  }
  return true;
}

//存在并发问题
//为新进入的客户分配的客服,有则返回stuff_no,无则返回false
/**
 *@param void
 *@return mixed:stuff_no,false
 */
 function assignStuff(){
    global $stuff;
    global $c_s_match;

    $stuffs=array_keys($stuff);
    for($i=0;$i<count($stuffs);$i++){
        if($stuff[$stuffs[$i]]['status'] == WAITTING ){
            return $stuffs[$i];
        }
    }
    unset($stuffs);
    return false;
 }
 //转接客服
 /**
  * @param   cur_stuff_no,trans_stuff_no,trans_customer_ip
  * @return  boolean
  */
 function transStuff($cur_stuff_no,$trans_stuff_no,$trans_customer_ip){
    global $stuff;
    global $c_s_match;

    $ps=-1;
    foreach ($stuff[$cur_stuff_no]['matcher'] as $k => $v) {
        $ps++;
        if($k==$trans_customer_ip){
          break;
        }
    }

    if($ps >= 0){
        $stuff[$trans_stuff_no]['matcher'][$trans_customer_ip]=$stuff[$cur_stuff_no]['matcher'][$trans_customer_ip];
        array_splice($stuff[$cur_stuff_no]['matcher'],$ps,1);
        unset($ps);
        return true;
    }else{
      return false;
    }

 }

 //检测客户是否已经存在
 /**
  *@param customer_ip
  *@return mixed:c_s_match_index,false
  */
 function existCustomer($customer_ip){
    global $stuff;
    global $c_s_match;

    for($i=0;$i<count($c_s_match);$i++){
        if($c_s_match[$i]['customer_ip']==$customer_ip){
            return $i;
        }
    }
    return false;
 }

//检测客服是否存在
/**
 *@param stuff_no
 *@return boolean
 */

 function existStuff($stuff_no){
    global $stuff;
  
    $stuff_keys=array_keys($stuff);
    if(in_array($stuff_no,$stuff_keys)){
      unset($stuff_keys);
      return true;
    }
    unset($stuff_keys);
    return false;
    /*if(array_key_exists($stuff_no,$stuff)){
      return true;
    }
    return false;*/
 }

 //检查用户类型
 /**
  *@param raw_data
  *@return number: 1,2,-1
  */
  function checkUserType($raw_data){
      if($raw_data['type']=='ms'){
          if($raw_data['sender_type']==CUSTOMER){
              return CUSTOMER;
          }else if($raw_data['sender_type']==STUFF){
              return STUFF;
          }else{
              return UNKOWN;
          }
      }
      if(!isset($raw_data['user_type'])){
          return UNKOWN;
      }
      if($raw_data['user_type']==CUSTOMER){
          if(!isset($raw_data['customer_ip'])){
              return UNKOWN;
          }
          return CUSTOMER;
      }else if($raw_data['user_type']==STUFF){
          if(!isset($raw_data['stuff_no'])){
              return UNKOWN;
          }
          return STUFF;
      }else{
          return UNKOWN;
      }
  }
  //检查双方之间是否匹配
  function checkMatches($me_type,$he_type,$me_id,$he_id){
      global $stuff;
      global $c_s_match;

      $m_k=0;
      $s_k=0;
      if($me_type == CUSTOMER && $he_type == STUFF){
          $m_k=$he_id;
          $s_k=$me_id;
      }else if($me_type == STUFF && $he_type == CUSTOMER){
          $m_k=$me_id;
          $s_k=$he_id;
      }else{
          return false;
      }
      if(isset($stuff[$m_k]['matcher'][$s_k])){
          return true;
      }else{
          return false;
      }
  }

  //创建stuff
  /**
   *@param no,con,matcher=null,chat_record=null,status=null)
   *@return array:stuff
   */
   function createStuff($no,$con,$status=WAITTING){
      $stuff=array(
          'no'=>$no,
          'con'=>$con,
          'matcher'=>array(),
          'chat_record'=>array(),
          'status'=>$status,
          'time'=>time(),
      );

      return $stuff;
   }
   //创建customer
   /**
    *@param customer_ip,con,status
    *@return array:customer
    */

    function createCustomer($customer_ip,$con,$status=WAITTING){
        $customer=array(
            'customer_ip'=>$customer_ip,
            'con'=>$con,
            'status'=>$status,
            'time'=>time(),
        );

        return $customer;
    }

    //检测客服是否处于可咨询状态
    /**
     *@param stuff_no
     *@return boolean
     **/
    function stuffIsWait($stuff_no){
        global $stuff;

        if(!isset($stuff[$stuff_no]) || $stuff[$stuff_no]['status'] != WAITTING || !empty($stuff[$stuff_no]['matcher'])){
            return false;
        }
        return true;
    }
    //检测客户是否登录
    /**
     *@param customer_ip
     *@return boolean
     */
    function isCustomerLogin($customer_ip){
        global $c_s_match;

        for($i=0;$i<count($c_s_match);$i++){
            if($c_s_match[$i]['customer_ip']==$customer_ip){
                return true;
            }
        }
        return false;
    }
    //检测客服是否登录
    /**
     *@param stuff_no
     *@return boolean
     */
    function isStuffLogin($stuff_no){
        global $stuff;

        if(array_key_exists($stuff_no,$stuff)){
            return true;
        }
        return false;
    }

    //登录检测
    function isLogin($type,$raw_data){
        global $stuff;
        global $c_s_match;

        switch($type){
           case CUSTOMER:
              return isCustomerLogin(isset($raw_data['customer_ip'])?$raw_data['customer_ip']:'');
              break;
           case STUFF:
              return isStuffLogin(isset($raw_data['stuff_no'])?$raw_data['stuff_no']:'');
              break;
           default:
              return false;
              break;
        }
    }
    //注销
    function logout($me){
        if($me['type']==CUSTOMER){
            clearCustomer($me['id']);
            //清除临时session或jwt
        }else if($me['type']==STUFF){
            clearStuff($me['id']);
            //清除临时session或jwt
        }else{
            //清除临时session或jwt
        }
        $me['con']->send('已注销登录');
    }

    //setsession
    function setSession($user_type,$user_id){
        $user_info=array(
            'type'=>$user_type,
            'id'=>$user_id,
        );

        session_start();
        $_SESSION['user']=json_encode($user_info);
        unset($user_info);
        return json_encode($user_info);
    }
    //getSession
    function getSession($k){
        return isset($_SESSION[$k]) ? null : json_decode($_SESSION[$k]);
    }
    //获取当前主体的基本信息
    function getMe(){
      return getSession($k);
    }
    //设置当前主体的session的基本信息
    function setMe($user_type,$user_id){
        return setSession($user_type,$user_id);
    }
    //更新客户的心跳时间
    /**
     *@param customer_ip
     *@return mixed:Time,Boolean
     */
    function updateCustomerExpires($customer_ip){
        global $c_s_match;
        global $stuff;

        $finger_print=existCustomer($customer_ip);
        if(!$finger_print){
            return false;
        }
        if(!array_key_exists($c_s_match[$finger_print],$stuff)){
            return false;
        }
        if(!array_key_exists($customer_ip,$c_s_match[$finger_print]['matcher'])){
            return false;
        }
        $t=time();
        $c_s_match[$finger_print]['matcher'][$customer_ip]['time']=$t;
        return $t;
    }
    //清空stuff的所有记录
    /**
     *@param stuff_no
     *@return boolean
    */
    function clearStuff($stuff_no){
         global $stuff;
         global $c_s_match;
         $count=0;

        if(array_key_exists($stuff_no,$stuff)){
            unset($GLOBALS['stuff'][$stuff_no]);
            $count++;
        }
        for($i=0;$i<count($c_s_match);$i++){
            if($c_s_match[$i]['stuff_no']==$stuff_no){
                unset($GLOBALS['c_s_match'][$i]['stuff_no']);
            }
        }
        return $count;
    }
    //清空customer的所有记录
    /**
     *@param customer_ip
     *@return number
     */
    function clearCustomer($customer_ip){
      //strit
      global $stuff;
      global $c_s_match;
      $count=0;

      $match_id=existCustomer($customer_ip);

      if($match_id !== false){
          unset($GLOBALS['stuff'][$c_s_match[$match_id]['stuff_no']]['matcher'][$customer_ip]);
          $count++;
      }
      //二次检测stuff中残余的,深搜stuff
      $stuff_keys=array_keys($stuff);
      for($i=0;$i<count($stuff_keys);$i++){
          if(array_key_exists($customer_ip,$stuff[$stuff_keys[$i]]['matcher'])){
              unset($GLOBALS['stuff'][$stuff_keys[$i]]['matcher'][$customer_ip]);
              $count++;
          }
      }

      return $count;
      unset($stuff_keys);
    }

    //更新客服的心跳时间
    /**
     *@param stuff_no
     *@return mixed:Time,Boolean
     */
    function updateStuffExpires($stuff_no){
        global $stuff;

        if(!existStuff($stuff_no)){
            return false;
        }
        $t=time();
        $stuff[$stuff_no]['time']=$t;
        return $t;
    }
    //客户上线后通知客服
    /**
     *@param customer_ip,stuff_no,message
     *@return boolean;
     */
    function notifyStuff($customer_ip,$stuff_no,$message='ok'){
        global $stuff;
        global $c_s_match;

        if(!isset($stuff[$stuff_no]) || !stuffIsWait($stuff_no)){
            return false;
        }
        $stuff[$stuff_no]['con']->send($message);
        return true;
    }
    //通知客户
    /**
     *@param stuff_no,customer_ip,message
     *@return boolean
     */
    function notifyCustomer($stuff_no,$customer_ip,$message){
        global $stuff;
        global $c_s_match;

        $cus=existCustomer($customer_ip);
        if($c_s_match[$cus]['stuff_no']!=$stuff_no){
            return false;
        }

        $stuff[$stuff_no]['matcher'][$customer_ip]['con']->send($message);
    }

    //创建ms消息
    /**
     *@param sender_id,receiver_id,content,send_time,received_time
     *@return msg_data
     */
    function createMsgData($sender_id,$receiver_id,$content,$status){

        return array(
           'sender_id'=>$sender_id,
           'receiver_id'=>$receiver_id,
           'content'=>$content,
           'send_time'=>time(),
           'received_time'=>null,
           'status'=>$status,
        );
    }

    //检查ms的消息数据的完整性与准确性
    /**
     *@param raw_data
     *@return boolean
     */
    function checkRawMsgData($raw_data){
        $needed_keys=array('type','sender_type','receiver_type','sender_id','receiver_id','content');
        $raw_data_keys=array_keys($raw_data);
        $differ=array_intersect_assoc($needed_keys,$raw_data_keys);
        if(count($differ) != count($needed_keys)){
            //可在开发环境里打印出错信息
            print(json_encode($differ));
            return false;
        }else{
            return true;
        }
        unset($needed_keys);
        unset($raw_data_keys);
        unset($differ);
    }
    //检查trans的数据结构的完整性与准确性
    /**
     * @param raw_data
     * @return  boolean
     */
    function checkTransData($raw_data){
       $needed_keys=array('type','cur_stuff_no','trans_stuff_no','trans_customer_ip');
       $raw_data_keys=array_keys($raw_data);
       $differ=array_intersect_assoc($needed_keys,$raw_data_keys);
       if(count($differ) != count($needed_keys)){
           return false;
       }else{
          return true;
       }

       unset($needed_keys);
       unset($raw_data_keys);
       unset($differ);
    }
    //stuff发送消息给customer函数
    /**
     *@param sender_id,receiver_id,content,send_time,received_time,status
     *@return boolean
     */
    function stuffSendMessage($sender_id,$receiver_id,$content){
        global $stuff;
        global $c_s_match;

        if(strlen($content)>MAX_LENGTH_OF_MSG){
            return false;
        }
        if(!array_key_exists($sender_id,$stuff)){
            return false;
        }
        if(!array_key_exists($receiver_id,$stuff[$sender_id]['matcher'])){
            return false;
        }

        $msg_record_data=createMsgData($sender_id,$receiver_id,$content,SENDED);
        //attention
        $stuff[$sender_id]['matcher'][$receiver_id]['con']->send(json_encode($msg_record_data));
        if(!isset($stuff[$sender_id]['chat_record'][$receiver_id])){
            $stuff[$sender_id]['chat_record'][$receiver_id]=array();
        }
        array_push($stuff[$sender_id]['chat_record'][$receiver_id],$msg_record_data);
        unset($msg_record_data);
        return true;
    }

    //customer发送给stuff的函数
    /**
     *@param sender_id,receiver_id,content,send_time,received_time,status
     *@return boolean
     */
    function customerSendMessage($sender_id,$receiver_id,$content){
        global $stuff;
        global $c_s_match;

        if(strlen($content)>MAX_LENGTH_OF_MSG){
          return false;
        }
        $cus=existCustomer($sender_id);
        if($cus === false){
            return false;
        }
        if($c_s_match[$cus]['stuff_no']!=$receiver_id){
            return false;
        }
        if(!existStuff($receiver_id)){
            return false;
        }

        $msg_record_data=createMsgData($sender_id,$receiver_id,$content,SENDED);

        $stuff[$receiver_id]['con']->send(json_encode($msg_record_data));
        if(!isset($stuff[$receiver_id]['chat_record'][$sender_id])){
            $stuff[$receiver_id]['chat_record'][$sender_id]=array();
        }
        array_push($stuff[$receiver_id]['chat_record'][$sender_id],$msg_record_data);
        unset($msg_record_data);
        return true;
    }

    //封装发送消息
    /**
     *@param sender_id,receiver_id,content
     *@return boolean
     */
   function sendMessage($sender_type,$receiver_type,$sender_id,$receiver_id,$content){
      if($sender_type == CUSTOMER && $receiver_type == STUFF){
          return customerSendMessage($sender_id,$receiver_id,$content);
      }else if($sender_type == STUFF && $receiver_type == CUSTOMER){
          return stuffSendMessage($sender_id,$receiver_id,$content);
      }else{
          //暂时不支持的渠道
          return false;
      }
      return false;
   }
   //ping函数侦测对方的状态
   /**
    *@param countpart_type,countpart_id
    *@return STATUS
    */
  function pong($me,$countpart_type,$countpart_id){
      global $stuff;
      global $c_s_match;

      if($countpart_type == CUSTOMER){
          if(!checkMatches(STUFF,CUSTOMER,$me,$countpart_id)){
              $stuff[$me]['con']->send(PING_FAIL);
          }
          $stuff[$me]['con']->send($stuff[$countpart_id]['matcher'][$me]['status']);
      }else if($countpart_type == STUFF){
        if(!checkMatches(CUSTOMER,STUFF,$countpart_id,$me)){
            $stuff[$countpart_id]['matcher'][$me]['con']->send(PING_FAIL);
        }
        $stuff[$countpart_id]['matcher'][$me]['con']->send($stuff[$countpart_id]['status']);
      }

      return ture;
  }
/*辅助函数部分-end*/
